/**
  @file fp.h
  @brief this file contains the declarations and documentation for finite field operations in SM2 algorithm.

  Finite field elements:
  - zero: A constant representing the value zero in the finite field.
  - one: A constant representing the value one in the finite field.
  - sm2_p: A constant representing the prime value of the SM2 algorithm.
  - sm2_n: A constant representing the order of the the SM2 algorithm.

  Function Declarations:
  - fp_to_str(): Converts a finite field element to its string representation.
  - fp_fm_str(): Converts a string representation to a finite field element.
  - fp_to_byt(): Converts a finite field element to a byte array.
  - fp_fm_byt(): Converts a byte array to a finite field element.
  - fp_to_bit(): Converts a finite field element to a bit representation.
  - fp_fm_bit(): Converts a bit representation to a finite field element.
  - fp_to_der(): Converts a finite field element to DER format.
  - fp_fm_der(): Converts DER-encoded data to a finite field element.
  - byt_to_str(): Converts a byte to its string representation.
  - byt_fm_str(): Converts a string representation to a byte.
  - fp_naf(): Performs Non-Adjacent Form (NAF) representation of a finite field element.
  - fp_is_odd(): Checks if a finite field element is odd.
  - fp_is_even(): Checks if a finite field element is even.
  - fp_tst(): Tests if a finite field element is in the finite field.
  - fp_cmp(): Compares two finite field elements.
  - fp_equ(): Checks if two finite field elements are equal.
  - fp_set(): Sets a finite field element to the value of another finite field element.
  - fp_add(): Adds two finite field elements, modulo sm2_p.
  - fp_sub(): Subtracts the second finite field element from the first, modulo sm2_p.
  - fp_neg(): Negates a finite field element.
  - fp_haf(): Computes half of a finite field element.
  - fp_dou(): Doubles a finite field element.
  - fp_inv(): Computes the inverse of a finite field element.
  - fp_mul(): Multiplies two finite field elements.
  - fp_rand(): Generates a random finite field element.
  - fp_sqr(): Calculates the square of a finite field element.
  - fp_tri(): Calculates the cube of a finite field element.
  - fp_qdt(): Calculates the fourth power of a finite field element.
  - fn_add(): Adds two finite field elements, modulo sm2_n.
  - fn_sub(): Subtracts the second finite field element from the first, modulo sm2_n.
  - fn_inv(): Computes the inverse of a finite field element, modulo sm2_n.
  - fn_mul(): Multiplies two finite field elements, modulo sm2_n.
**/

#ifndef SM_FP_H
#define SM_FP_H

#include <stdio.h>
#include "common.h"
#include "asn1.h"

extern const fp zero;
extern const fp one;
extern const fp sm2_p;
extern const fp sm2_n;

/**
 * @brief Converts a finite field element to its string representation.
 * @param n The finite field element to be converted.
 * @param s The output string where the result will be stored.
 */
void fp_to_str(const fp n, str s);

/**
 * @brief Converts a string representation to a finite field element.
 * @param n The finite field element to be updated with the string value.
 * @param str The input string representation of the finite field element.
 */
void fp_fm_str(fp n, const str str);

/**
 * @brief Converts a finite field element to a byte array.
 * @param n The finite field element to be converted.
 * @param b The output byte array where the result will be stored.
 */
void fp_to_byt(const fp n, byt b);

/**
 * @brief Converts a byte array to a finite field element.
 * @param n The finite field element to be updated with the byte value.
 * @param b The input byte array representing the finite field element.
 */
void fp_fm_byt(fp n, const byt b);

/**
 * @brief Converts a finite field element to a bit representation.
 * @param n The finite field element to be converted.
 * @param b The output bit representation where the result will be stored.
 */
void fp_to_bit(const fp n, bit b);

/**
 * @brief Converts a bit representation to a finite field element.
 * @param n The finite field element to be updated with the bit value.
 * @param b The input bit representation of the finite field element.
 */
void fp_fm_bit(fp n, const bit b);

/**
 * @brief Converts a finite field element to DER (Distinguished Encoding Rules) format.
 * @param n The finite field element to be converted.
 * @param dst The output buffer where the DER-encoded data will be stored.
 * @param dlen The length of the DER-encoded data.
 */
void fp_to_der(const fp n, u8 **dst, u64 *dlen);

/**
 * @brief Converts DER-encoded data to a finite field element.
 * @param n The finite field element to be updated with the DER-encoded value.
 * @param src The input DER-encoded data.
 * @param slen The length of the DER-encoded data.
 */
void fp_fm_der(fp n, const u8 **src, u64 *slen);

/**
 * @brief Converts a byte to its string representation.
 * @param b The byte to be converted.
 * @param s The output string where the result will be stored.
 */
void byt_to_str(const byt b, str s);

/**
 * @brief Converts a string representation to a byte.
 * @param b The byte to be updated with the string value.
 * @param s The input string representation of the byte.
 */
void byt_fm_str(byt b, const str s);

/**
 * @brief Performs Non-Adjacent Form (NAF) representation of a finite field element.
 * @param r The output buffer to store the NAF result.
 * @param w The weight used for the NAF representation.
 * @param n The finite field element to be converted to NAF.
 */
void fp_naf(naf r, u8 w, const fp n);

/**
 * @brief Checks if a finite field element is odd.
 * @param c The finite field element to be checked.
 * @return True if the element is odd, false otherwise.
 */
#define fp_is_odd(c) ((c[0] & 1) == 1)

/**
 * @brief Checks if a finite field element is even.
 * @param c The finite field element to be checked.
 * @return True if the element is even, false otherwise.
 */
#define fp_is_even(c) ((c[0] & 1) == 0)

#define fp_is_0(a) ((a[0] == 0) && (a[1] == 0) && (a[2] == 0) && (a[3] == 0) && (a[4] == 0) && (a[5] == 0) && (a[6] == 0) && (a[7] == 0))

/**
 * @brief Tests if a is in finite field, i.e., 0 <= a < sm2_p
 * @param a The finite field element to be tested.
 * @return 1 if the a is in finite field, 0 otherwise.
 */
int fp_tst(const fp a);

/**
 * @brief Compares two finite field elements.
 * @param a The first finite field element to be compared.
 * @param b The second finite field element to be compared.
 * @return 1 if a >= b, 0 otherwise.
 */
int fp_cmp(const fp a, const fp b);

/**
 * @brief Checks if two finite field elements are equal.
 * @param a The first finite field element to be compared.
 * @param b The second finite field element to be compared.
 * @return 1 if the elements are equal, 0 otherwise.
 */
int fp_equ(const fp a, const fp b);

/**
 * @brief Sets a finite field element to the value of another finite field element.
 * @param a The finite field element to be set.
 * @param b The finite field element to copy the value from.
 */
void fp_set(fp a, const fp b);

/**
 * @brief Adds two finite field elements and stores the result in r.
 * r ≡ a + b mod sm2_p
 * @param r The output buffer where the sum will be stored.
 * @param a The first finite field element to be added.
 * @param b The second finite field element to be added.
 */
void fp_add(fp r, const fp a, const fp b);

/**
 * @brief Subtracts the second finite field element from the first and stores the result
 * r ≡ a - b mod sm2_p
 * @param r The output buffer where the difference will be stored.
 * @param a The finite field element to be subtracted from.
 * @param b The finite field element to subtract.
 */
void fp_sub(fp r, const fp a, const fp b);

/**
 * @brief Negates a finite field element and stores the result in r.
 * r = sm2_p - a ≡  -a mod sm2_p
 * @param r The output buffer where the negated value will be stored.
 * @param a The finite field element to be negated.
 */
void fp_neg(fp r, const fp a);

/**
 * @brief Computes half of a finite field element and stores the result in r.
 * r = a / 2 if a is even, r = (a + sm2_p) / 2 if a is odd, thus a ≡ 2r mod sm2_p
 * @param r The output buffer where the halved value will be stored.
 * @param a The finite field element to be halved.
 */
void fp_haf(fp r, const fp a);

/**
 * @brief Doubles a finite field element and stores the result in r. This function is faster than fp_add.
 * r ≡ 2a mod sm2_p
 * @param r The output buffer where the doubled value will be stored.
 * @param a The finite field element to be doubled.
 */
void fp_dou(fp r, const fp a);

/**
 * @brief Computes the inverse of a finite field element and stores the result in r.
 * @param r The output buffer where the inverse will be stored.
 * @param q The finite field element to be inverted.
 */
void fp_inv(fp r, const fp q);

/**
 * @brief Multiplies two finite field elements and stores the result in r.
 * @param r The output buffer where the product will be stored.
 * @param a The first finite field element to be multiplied.
 * @param b The second finite field element to be multiplied.
 */
void fp_mul(fp r, const fp a, const fp b);

/**
 * @brief Generates a random finite field element and stores it in r.
 * @param r The output buffer where the random value will be stored.
 * @param p The parameter used for generation, typically the prime of the field.
 */
void fp_rand(fp r, const fp p);

/**
 * @brief Calculates the square of a finite field element and stores the result in r.
 * @param r The output buffer where the squared value will be stored.
 * @param a The finite field element to be squared.
 */
#define fp_sqr(r, a) fp_mul(r, a, a)

/**
 * @brief Calculates the cube of a finite field element and stores the result in r.
 * @param r The output buffer where the cubed value will be stored.
 * @param a The finite field element to be cubed.
 */
#define fp_tri(r, a) fp_mul(r, a, a); fp_mul(r, r, a)

/**
 * @brief Calculates the fourth power of a finite field element and stores the result in r.
 * @param r The output buffer where the fourth power value will be stored.
 * @param a The finite field element to be raised to the fourth power.
 */
#define fp_qdt(r, a) fp_sqr(r, a, a)

/**
 * @brief Adds two finite field elements and stores the result in r.
 * r ≡ a + b mod sm2_n
 * @param r The output buffer where the sum will be stored.
 * @param a The first finite field element to be added.
 * @param b The second finite field element to be added.
 */
void fn_add(fp r, const fp a, const fp b);

/**
 * @brief Subtracts the second finite field element from the first and stores the result in r.
 * r ≡ a - b mod sm2_n
 * @param r The output buffer where the difference will be stored.
 * @param a The finite field element to be subtracted from.
 * @param b The finite field element to subtract.
 */
void fn_sub(fp r, const fp a, const fp b);

/**
 * @brief Computes the inverse of a finite field element and stores the result in r.
 * r ≡ q ^ (-1) mod sm2_n
 * @param r The output buffer where the inverse will be stored.
 * @param q The finite field element to be inverted.
 */
void fn_inv(fp r, const fp q);

/**
 * @brief Multiplies two finite field elements and stores the result in r.
 * r ≡ a * b mod sm2_n
 * @param r The output buffer where the product will be stored.
 * @param a The first finite field element to be multiplied.
 * @param b The second finite field element to be multiplied.
 */
void fn_mul(fp r, const fp a, const fp b);

#endif //SM_FP_H